#include <crt_util.h>

#if (TARGET_OS == OS_WIN)

#pragma comment(lib, "ws2_32.lib")
#include <errno.h>
#include <stdlib.h>
#include <winsock2.h>

ssize_t recvmsg(int fd, struct msghdr* msg, int flags)
{
    DWORD rcvd;
    int ret;
    DWORD dwFlags;
    INT fromlen;
    WSABUF* buf = NULL;
    unsigned int i;

    if (msg->msg_controllen != 0) {
        errno = ENOSYS;
        return -1;
    }

    if (msg->msg_iovlen > IOV_MAX) {
        errno = EINVAL;
        return -1;
    }

    buf = heap_malloc(msg->msg_iovlen * sizeof(*buf));
    if (buf == NULL)
        return -1;

    for (i = 0; i < msg->msg_iovlen; i++) {
        buf[i].len = msg->msg_iov[i].iov_len;
        buf[i].buf = msg->msg_iov[i].iov_base;
    }

    dwFlags = flags;
    fromlen = msg->msg_namelen;

    if (fromlen)
        ret = WSARecvFrom(fd, buf, msg->msg_iovlen, &rcvd, &dwFlags, msg->msg_name, &fromlen, NULL, NULL);
    else
        ret = WSARecv(fd, buf, msg->msg_iovlen, &rcvd, &dwFlags, NULL, NULL);

    heap_free(buf);

    if (ret == 0) {
        msg->msg_namelen = fromlen;
        msg->msg_flags = dwFlags;
        return rcvd;
    }

    switch (WSAGetLastError()) {
    case WSAEWOULDBLOCK:
        errno = EAGAIN;
        break;
    }
    return -1;
}

ssize_t sendmsg(int fd, const struct msghdr* msg, int flags)
{
    WSABUF* buf = NULL;
    DWORD sent;
    unsigned int i;
    int ret;

    if (msg->msg_controllen != 0) {
        errno = ENOSYS;
        return -1;
    }

    if (msg->msg_iovlen > IOV_MAX) {
        errno = EINVAL;
        return -1;
    }

    buf = heap_malloc(msg->msg_iovlen * sizeof(*buf));
    if (buf == NULL)
        return -1;

    for (i = 0; i < msg->msg_iovlen; i++) {
        buf[i].len = msg->msg_iov[i].iov_len;
        buf[i].buf = (void*)msg->msg_iov[i].iov_base;
    }

    ret = WSASendTo(fd, buf, msg->msg_iovlen, &sent, flags, msg->msg_name, msg->msg_namelen, NULL, NULL);

    heap_free(buf);

    if (ret == 0)
        return sent;

    switch (WSAGetLastError()) {
    case WSAEWOULDBLOCK:
        errno = EAGAIN;
        break;
    }
    return -1;
}

#endif
